package com.example.demo.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.example.demo.common.*;
import com.example.demo.common.constant.RedisKey;
import com.example.demo.common.constant.ResultCode;
import com.example.demo.entity.Role;
import com.example.demo.entity.User;
import com.example.demo.entity.config.LySecret;
import com.example.demo.entity.dto.LoginerDTO;
import com.example.demo.entity.dto.UserDTO;
import com.example.demo.entity.vo.EncryptVO;
import com.example.demo.entity.vo.LoginerVO;
import com.example.demo.exception.BusinessException;
import com.example.demo.mapper.RoleMapper;
import com.example.demo.mapper.UserMapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class UserService {
    private final RedisTemplate<String, String> redis;
    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final LySecret lySecret;
    private final SqlSessionFactory sqlSessionFactory;

    public Result getUserInfo(int id) {
        Map<String, Object> user = userMapper.getUserInfo(id);

        Result res = new Result();
        res.ok();
        res.put("info", user);
        return res;
    }

    public Result loginToken(LoginerDTO loginerDTO) {
        Result res = new Result();

        // 根据用户账号设置条件
        MPJLambdaWrapper<User> query = new MPJLambdaWrapper<>();
        query.select(User::getUserId, User::getName, User::getRoleId, User::getTokenId, User::getPassword);
        query.eq(User::getUsername, loginerDTO.getUsername());
        query.eq(User::getIsUse, 1);
        query.eq(User::getIsDelete, 0);

        // 判断用户是否存在
        User user = userMapper.selectOne(query);
        if (user == null) {
            return res.error(ResultCode.LOGIN_USER_NOT_EXIST);
        }

        // 判断是否设置了角色权限
        if (Valid.isEmpty(user.getRoleId())) {
            return res.error(ResultCode.LOGIN_USER_ROLE_EMPTY);
        }

        // 判断密码是否正确
        try {
            boolean checkpw = BCrypt.checkpw(loginerDTO.getPassword(), user.getPassword());
            if (!checkpw) {
                return res.error(ResultCode.LOGIN_PASSWORD_ERROR);
            }
        }
        catch (Exception ex) {
            return res.error(ResultCode.LOGIN_PASSWORD_FORMAT_ERROR);
        }

        // 获取用户角色权限
        MPJLambdaWrapper<Role> query2 = new MPJLambdaWrapper<>();
        query2.select(Role::getName, Role::getCode);
        query2.eq(Role::getRoleid, user.getRoleId());
        query2.eq(Role::getIsuse, 1);

        Role role = roleMapper.selectOne(query2);
        if (role == null) {
            return res.error(ResultCode.LOGIN_USER_ROLE_EMPTY);
        }

        // 更新用户信息
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUserId, user.getUserId());

        User record = new User();
        record.setLoginTime(Func.getNow());
        record.setDutyStatus(0);
        record.setUpdateTime(Func.getNow());
        if (userMapper.update(record, wrapper) < 1) {
            return res.error(ResultCode.LOGIN_USER_UPDATE_FAIL);
        }

        // 生成token，并存入redis
        String tokenId = user.getTokenId();
        String tokenValue = Func.getULID();
        redis.opsForValue().set(RedisKey.LOGIER_TOKEN + tokenId, tokenValue, 40, TimeUnit.MINUTES);
        redis.opsForValue().set(RedisKey.LOGIER_NAME + tokenId, user.getName(), 40, TimeUnit.MINUTES);
        redis.opsForValue().set(RedisKey.LOGIER_TIME + tokenId, String.valueOf(System.currentTimeMillis()), 40, TimeUnit.MINUTES);
        redis.opsForValue().set(RedisKey.LOGIER_ROLE + tokenId, role.getName() + "@" + role.getCode(), 40, TimeUnit.MINUTES);

        // 返回token给前端
        res.ok();
        res.put("token", tokenId + tokenValue);
        return res;
    }

    public Result loginTokenNoUserId(LoginerDTO loginerDTO) {
        // 获取登录用户信息
        User user = userMapper.getLoginInfo(loginerDTO.getUsername());

        // 判断用户是否存在
        Valid.ObjectNotNull(user, ResultCode.LOGIN_USER_NOT_EXIST);

        // 判断是否设置了角色权限
        Valid.StringNotEmpty(user.getRoleId(), ResultCode.LOGIN_USER_ROLE_EMPTY);

        // 判断密码是否正确
        try {
            boolean checkpw = BCrypt.checkpw(loginerDTO.getPassword(), user.getPassword());
            if (!checkpw) {
                throw new BusinessException(ResultCode.LOGIN_PASSWORD_ERROR);
            }
        }
        catch (Exception ex) {
            throw new BusinessException(ResultCode.LOGIN_PASSWORD_FORMAT_ERROR);
        }

        // 获取用户角色权限
        Role role = roleMapper.getRoleInfo(user.getRoleId());
        Valid.ObjectNotNull(role, ResultCode.LOGIN_USER_ROLE_EMPTY);

        // 更新用户信息
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUserId, user.getUserId());

        User record = new User();
        record.setLoginTime(Func.getNow());
        record.setDutyStatus(0);
        record.setUpdateTime(Func.getNow());
        if (userMapper.update(record, wrapper) < 1) {
            throw new BusinessException(ResultCode.LOGIN_USER_UPDATE_FAIL);
        }

        // 生成jwt
        String tokenId = user.getTokenId();

        JSONObject head = new JSONObject();
        head.put("alg", SignatureAlgorithm.HS256.getValue());
        head.put("typ", "JWT");

        String jwt = Jwts.builder()
                .setHeader(head)
                .setId(Func.getULID())
                .signWith(SignatureAlgorithm.HS256, lySecret.getJwtPassword())
                .compact();

        // 将32位的tokenId（UUID）作为token的前缀
        String token = tokenId + jwt;

        // 将token与有效期存入redis（不能将敏感数据放在JWT的body里）
        LoginerVO loginer = new LoginerVO();
        loginer.setToken(token);
        loginer.setTime(System.currentTimeMillis());

        redis.opsForValue().set(RedisKey.LOGIER + tokenId, JSONObject.toJSONString(loginer), 40, TimeUnit.MINUTES);

        // 返回token给前端
        Result res = new Result().ok();
        res.put("token", token);
        return res;
    }

    public Result loginJwt(LoginerDTO loginerDTO) {
        Result res = new Result();

        // 获取登录用户信息
        User user = userMapper.getLoginInfo(loginerDTO.getUsername());

        // 判断用户是否存在
        if (user == null) {
            return res.error(ResultCode.LOGIN_USER_NOT_EXIST);
        }

        // 判断是否设置了角色权限
        if (Valid.isEmpty(user.getRoleId())) {
            return res.error(ResultCode.LOGIN_USER_ROLE_EMPTY);
        }

        // 判断密码是否正确
        try {
            boolean checkpw = BCrypt.checkpw(loginerDTO.getPassword(), user.getPassword());
            if (!checkpw) {
                return res.error(ResultCode.LOGIN_PASSWORD_ERROR);
            }
        }
        catch (Exception ex) {
            return res.error(ResultCode.LOGIN_PASSWORD_FORMAT_ERROR);
        }

        // 获取用户角色权限
        Role role = roleMapper.getRoleInfo(user.getRoleId());
        if (role == null) {
            return res.error(ResultCode.LOGIN_USER_ROLE_EMPTY);
        }

        // 更新用户信息
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUserId, user.getUserId());

        User record = new User();
        record.setLoginTime(Func.getNow());
        record.setDutyStatus(0);
        record.setUpdateTime(Func.getNow());
        if (userMapper.update(record, wrapper) < 1) {
            return res.error(ResultCode.LOGIN_USER_UPDATE_FAIL);
        }

        // 生成jwt
        String userId = user.getUserId();

        JSONObject head = new JSONObject();
        head.put("alg", SignatureAlgorithm.HS256.getValue());
        head.put("typ", "JWT");

        JSONObject body = new JSONObject();
        body.put("userid", userId);

        String token = Jwts.builder()
                .setHeader(head)
                .setClaims(body)
                .setId(Func.getULID())
                .signWith(SignatureAlgorithm.HS256, lySecret.getJwtPassword())
                .compact();

        // 生成token，并存入redis
        LoginerVO loginer = new LoginerVO();
        loginer.setToken(token);
        loginer.setName(user.getName());
        loginer.setRoleName(role.getName());
        loginer.setRoleCode(role.getCode());
        loginer.setTime(System.currentTimeMillis());

        redis.opsForValue().set(RedisKey.LOGIER + userId, JSONObject.toJSONString(loginer), 40, TimeUnit.MINUTES);

        // 返回token给前端
        res.ok();
        res.put("token", token);
        return res;
    }

    public Result loginJson(LoginerDTO loginerDTO) {
        Result res = new Result();

        // 获取登录用户信息
        User user = userMapper.getLoginInfo(loginerDTO.getUsername());

        // 判断用户是否存在
        if (user == null) {
            return res.error(ResultCode.LOGIN_USER_NOT_EXIST);
        }

        // 判断是否设置了角色权限
        if (Valid.isEmpty(user.getRoleId())) {
            return res.error(ResultCode.LOGIN_USER_ROLE_EMPTY);
        }

        // 判断密码是否正确
        try {
            boolean checkpw = BCrypt.checkpw(loginerDTO.getPassword(), user.getPassword());
            if (!checkpw) {
                return res.error(ResultCode.LOGIN_PASSWORD_ERROR);
            }
        }
        catch (Exception ex) {
            return res.error(ResultCode.LOGIN_PASSWORD_FORMAT_ERROR);
        }

        // 获取用户角色权限
        Role role = roleMapper.getRoleInfo(user.getRoleId());
        if (role == null) {
            return res.error(ResultCode.LOGIN_USER_ROLE_EMPTY);
        }

        // 更新用户信息
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUserId, user.getUserId());

        User record = new User();
        record.setLoginTime(Func.getNow());
        record.setDutyStatus(0);
        record.setUpdateTime(Func.getNow());
        if (userMapper.update(record, wrapper) < 1) {
            return res.error(ResultCode.LOGIN_USER_UPDATE_FAIL);
        }

        // 生成token，并存入redis
        String tokenId = user.getTokenId();
        String tokenValue = Func.getULID();

        LoginerVO loginer = new LoginerVO();
        loginer.setToken(tokenValue);
        loginer.setUserId(user.getUserId());
        loginer.setName(user.getName());
        loginer.setRoleName(role.getName());
        loginer.setRoleCode(role.getCode());
        loginer.setTime(System.currentTimeMillis());

        redis.opsForValue().set(RedisKey.LOGIER + tokenId, JSONObject.toJSONString(loginer), 40, TimeUnit.MINUTES);

        // 返回token给前端
        res.ok();
        res.put("token", tokenId + tokenValue);
        return res;
    }

    public Result getUserList(UserDTO userDTO) {
        User user = new User();
        user.setName(userDTO.getName());
        user.setIsUse(!Valid.isEmpty(userDTO.getAge()) ? Integer.parseInt(userDTO.getAge()) : null);

        // 传参数<=5时，用@Param单列参数；否则，用实体类传参
        List<Map<String, Object>> list = userMapper.getUserList(user);

        Result res = new Result();
        res.ok();
        res.put("list", list);
        return res;
    }

    public Result insertUser() {
        // 批量插入要分批次，每批次1000~2000条
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            User user = new User();
            user.setName("林春丽" + i);
            user.setUsername("全晴");
            userList.add(user);

            if (i != 0 && i % 1000 == 0 || i == 9999) {
                userMapper.insertUsers(userList);
                userList.clear();
            }
        }

        Result res = new Result();
        res.ok();
        return res;
    }

    public Result insertUserByMyBatis() {
        // 注：这个方法还不如 MyBatis 的 <foreach>的分批插入快
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        for (int i = 0; i < 10000; i++) {
            User user = new User();
            user.setName("林春丽" + i);
            user.setUsername("全晴");
            mapper.insert(user);
        }

        sqlSession.commit();
        sqlSession.close();

        Result res = new Result();
        res.ok();
        return res;
    }

    public Result insertUserByJDBC() throws SQLException {
        Connection con = null;
        try {
            String url = "jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false&useServerPrepStmts=false&rewriteBatchedStatements=true";
            String username = "root";
            String password = "214luohailin";
            con = DriverManager.getConnection(url, username, password);
            con.setAutoCommit(false);

            String sql = "insert into user (username, name) values (?, ?)";
            PreparedStatement ps = con.prepareStatement(sql);

            for (int i = 0; i < 10000; i++) {
                ps.setString(1, "全晴" + i);
                ps.setString(2, "林春丽" + i);
                ps.addBatch();
            }

            ps.executeBatch();
            ps.clearBatch();
            con.commit();
            con.close();
        }
        catch (Exception ex) {
            Func.print(ex);
            if (con != null) {
                con.close();
            }
        }

        Result res = new Result().ok();
        return res;
    }

    public Result JDBC() {
        JSONObject js = new JSONObject();
        js.put("name", "周罗丹");
        js.put("work", "税务专员");

        BigDecimal money = new BigDecimal("5120.25");

        Object[] param = {"林春丽", 39, 4012.5, js, 4012.5f, money};

        for (int i = 0; i < param.length; i++) {
            Object obj = param[i];
            if (obj instanceof String) {
                Func.print("第" + (i + 1) + "个参数(String)：" + obj);
            }
            else if (obj instanceof Integer) {
                Func.print("第" + (i + 1) + "个参数(Integer)：" + obj);
            }
            else if (obj instanceof Float) {
                // 4012.5f才是Float，4012.5默认是Double，但输出时都显示4012.5
                Func.print("第" + (i + 1) + "个参数(Float)：" + obj);
            }
            else if (obj instanceof Double) {
                Func.print("第" + (i + 1) + "个参数(Double)：" + obj);
            }
            else if (obj instanceof JSONObject) {
                Func.print("第" + (i + 1) + "个参数(JSONObject)：" + obj);
            }
            else if (obj instanceof BigDecimal) {
                Func.print("第" + (i + 1) + "个参数(BigDecimal)：" + obj);
            }
            else {
                Func.print("第" + (i + 1) + "个参数(异常)：" + obj.getClass());
            }
        }

        Result res = new Result().ok();
        return res;
    }

    public Result sensitiveUser() {
        EncryptVO user = new EncryptVO();
        user.setUsername("lhlysyl543214dahai");
        user.setIdcard("450502198603230316");
        user.setPhone("3072596");
        user.setMobile("13507797221");
        user.setAddress("北海市海城区贵州路屋仔村二十一巷13号");
        user.setEmail("543982146@qq.com");
        user.setBankcard("4563512600683239892");
        user.setPassword("214luohailin");
        user.setKey(Sensitive.key("0502100229"));

        Result res = new Result();
        res.ok();
        res.put("user", user);
        return res;
    }
}
